package com.strato.rpc.hessian.v1_0_0;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.ConnectException;
import java.net.MalformedURLException;

import com.caucho.hessian.client.HessianProxyFactory;
import com.caucho.hessian.client.HessianRuntimeException;
import com.strato.annotation.osgi.v1_0_0.service.OsgiService;
import com.strato.classloader.v1_0_0.OverallBundleContextClassLoader;
import com.strato.logging.v1_0_0.api.Log;
import com.strato.logging.v1_0_0.api.LogFactory;
import com.strato.rpc.hessian.v1_0_0.ha.RoundRobinHAStrategy;
import com.strato.rpc.transport.http.v1_0_0.HttpTransport;
import com.strato.rpc.transport.http.v1_0_0.RpcInvoker;

@OsgiService
public class HessianHttpTransport extends HttpTransport implements InvocationHandler{
	
	private Log logger=LogFactory.getLog(getClass());
	
	private String path;

	private Class<?> intf;

	private int retry;
	
	@Override
	protected RpcInvoker createInvoker(Object object, Class<?> intf) {
		HessianHttpInvoker invoker=new HessianHttpInvoker(object, intf);
		return invoker;
	}

	@Override
	public Object createProxy(String path, Class<?> intf,int retry) {
		return createHAProxy(path,intf,retry);
	}

	private Object createHAProxy(String path, Class<?> intf, int retry) {
		this.path=path;
		this.intf=intf;
		this.retry=retry;
		return Proxy.newProxyInstance(intf.getClassLoader(), new Class[] { intf }, this);
	}

	@Override
	public Object invoke(Object o, Method m, Object[] args) throws Throwable {
		return doInvoke(o,m,args,retry);
	}
	
	private Object doInvoke(Object o, Method m, Object[] args, int nextRetry) throws Throwable {
		HAStrategy strategy=getHAStrategy();
		String path=strategy.nextProxyId();
		Object proxy=strategy.getProxy(path);
		try{
			long start=System.currentTimeMillis();
			Object result = m.invoke(proxy, args);
			long end=System.currentTimeMillis();
			strategy.increaseCount(path,1);
			strategy.increaseCost(path,(end-start));
			return result;
		}catch(Exception e){
			if(InvocationTargetException.class.isInstance(e)){
				Throwable cause =e.getCause();
				if(cause!=null&&HessianRuntimeException.class.isInstance(cause)){
					HessianRuntimeException hre=(HessianRuntimeException) cause;
					Throwable rc = hre.getRootCause();
					if(rc!=null&&ConnectException.class.isInstance(rc)){
						logger.warn("Rpc target "+path+" disconnected!");
						strategy.markUnavailable(path);
						if(nextRetry>0){
							return doInvoke(o,m,args,nextRetry-1);
						}
					}
				}
			}
			throw e;
		}
	}

	private static HessianProxyFactory factory = new HessianProxyFactory();
	
	public Object createSingleProxy(String baseurl,String path, Class<?> intf) {
		try {
			Object proxy = factory.create(intf,baseurl+getUrlPrefix()+path,OverallBundleContextClassLoader.getInstance());
			return proxy;
		} catch (MalformedURLException e) {
			e.printStackTrace();
			return null;
		}
	}
	
	private HAStrategy strategy=null;
	private synchronized HAStrategy getHAStrategy() {
		if(strategy==null){
			strategy=new RoundRobinHAStrategy(path,intf,this);
		}
		return strategy;
	}

}
